package autoz.czt.wizards;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.osgi.framework.Bundle;

import autoz.core.AutozCorePlugin;
import autoz.core.wizards.AbstractNewProjectCreationOperation;
import autoz.core.wizards.NewProjectModel;
import autoz.czt.CZTPlugin;

public class NewProjectCreationOperation extends AbstractNewProjectCreationOperation  {

	private static final String FTL_TEMPLATES_DIR = "tpl";
	private static final String OUTPUT_DIR = "ZFiles";

	/**
	 * Create a new operation.
	 * @param model Project attributes
	 */
	public NewProjectCreationOperation(NewProjectModel model) {
		super(model);
	}

	/**
	 * Run the project creation task. This method is invoked by the Eclipse IDE after the 
	 * Project Creation Wizard is finished.
	 * 
	 * @param pMon progress monitor
	 * @throws InvocationTargetException
	 * @throws InterruptedException
	 */
	public void run(IProgressMonitor pMon) throws InvocationTargetException, InterruptedException {

		IProgressMonitor monitor = pMon;
		// this null-check is recommended by developers of other plugins 
		if (pMon == null) {
			monitor = new NullProgressMonitor();
		}

		try {
			monitor.beginTask("Creating project", 40);
			IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
			String name = model.getProjectName();
			IProject project = root.getProject(name);

			createProject(project, monitor);
			monitor.worked(40);

			monitor.beginTask("Creating directories", 60);
			createProjectDirs(project, monitor);
			monitor.worked(60);
		} catch (CoreException e) {
			e.printStackTrace();
		} finally {
			monitor.done();
		}
	}

	/**
	 * Create the project directory.
	 * If the user has specified an external project location,
	 * the project is created with a custom description for the location.
	 * 
	 * @param project project
	 * @param monitor progress monitor
	 * @throws CoreException
	 */
	protected void createProject(IProject project, IProgressMonitor monitor)
	throws CoreException, InterruptedException {

		if (!project.exists()) {
			project.create(monitor);
		} else {
			// Project exists - need to cancel. This should never occur as the wizard should catch it
			throw new InterruptedException("Project name already exists.  Please choose another.");
		}

		if (!project.isOpen()) {
			project.open(monitor);
		}
	}

	/**
	 * Create project's (sub)directory structure.
	 *  
	 * @param project project
	 * @param monitor progress monitor
	 * @throws CoreException
	 */
	protected void createProjectDirs(IProject project, IProgressMonitor monitor) throws CoreException {
		monitor.subTask("Creating project directories");

		// Directory for models
		if (model.getCreateModelDirectory())
			createDir(project, monitor, model.getModelDirectory());

		// Template directory
		if (model.getAddTemplateDirectory()){
			createDir(project,monitor,FTL_TEMPLATES_DIR);
			addTemplates(project, monitor, FTL_TEMPLATES_DIR);
		}
		
		// Output directory
		if (model.getCreateOutputDirectory()){
			createDir(project, monitor, model.getOutputDirectory());
			addOutputFiles(project, monitor, OUTPUT_DIR, model.getOutputDirectory());
			
			// Create blank output file in output directory
			IFile file = project.getFile(model.getOutputDirectory() +"/"+ model.getOutputFileName()); //TODO: need to sort out how to name this
			file.create(new ByteArrayInputStream(new byte[]{}), true, monitor);
		} else{
			// Create blank output file in root directory
			IFile file = project.getFile(model.getOutputFileName()); //TODO: need to sort out how to name this
			file.create(new ByteArrayInputStream(new byte[]{}), true, monitor);
		}
	}

	/**
	 * 
	 * @param project
	 * @param monitor
	 * @param outputDirectory
	 * @param newOutputDirectory
	 */
	private void addOutputFiles(IProject project, IProgressMonitor monitor,
			String outputDirectory, String newOutputDirectory) {
		Enumeration<String> outputEnum;
		
		try{
			outputEnum = CZTPlugin.getDefault().getBundle().getEntryPaths(outputDirectory);
			
			while (outputEnum.hasMoreElements()){
				String elem = outputEnum.nextElement().toString();
//				System.out.println(elem);
				if (!elem.contains(".svn") && !elem.contains(".DS_Store")){
					if (elem.endsWith("/")){
						// This bit might not be needed but is there just incase I add some
						// folders to the output dir
						createDir(project,monitor,elem);
						addOutputFiles(project,monitor,elem, newOutputDirectory);
					}
					else{
						String newName = elem.replace(OUTPUT_DIR, newOutputDirectory);
						createFile(project, monitor, elem, newName,false);
					}
				}
			}
			
		} catch (Exception e){
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * @param project
	 * @param monitor
	 * @param path
	 */
	private void addTemplates(IProject project, IProgressMonitor monitor, String path) {
		Enumeration<String> templateEnum;

		try{
			templateEnum = AutozCorePlugin.getDefault().getBundle().getEntryPaths(path);

			// Trawl through each folder and add all files and folders
			while (templateEnum.hasMoreElements()){
				String elem = templateEnum.nextElement().toString();
				
				// 	Don't want the subversion files to be included!
				if (!elem.contains(".svn") && !elem.contains(".DS_Store")){
					if (elem.endsWith("/")){
						createDir(project,monitor,elem);
						addTemplates(project,monitor,elem);
					}
					else{
						createFile(project, monitor, elem, elem,true);
					}
				}
			}

		} catch (Exception ex) {
//			System.out.println(ex.getMessage());
			ex.printStackTrace();
		}		
	}

	/**
	 * Create a sub-directory to the given project's directory.
	 * @param project project
	 * @param monitor progress monitor
	 * @param dir directory name
	 * @throws CoreException
	 */
	protected void createDir(IProject project, IProgressMonitor monitor, String dir) throws CoreException {
		if (dir != null && dir.length() > 0) {
			IFolder folder = project.getFolder(dir);
			folder.create(true, true, monitor);
		}
	}

	/**
	 * Create main file of the project from template.
	 * 
	 * @param project project
	 * @param monitor progress monitor
	 * @throws CoreException
	 */
	protected void createFile(IProject project, IProgressMonitor monitor, String bundleFilename, 
			String projectFilename, Boolean useCommonBundle) throws CoreException {

		try {
			Bundle bundle;
			
			if (useCommonBundle){
				bundle = AutozCorePlugin.getDefault().getBundle();
			} else {
				bundle = CZTPlugin.getDefault().getBundle();
			}
			InputStream stream = FileLocator.find(bundle, new Path(bundleFilename), null).openStream();
			
			IFile file = project.getFile(projectFilename);
			file.create(stream, true, monitor);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

    /**
     * Shamelessly stolen from Texclipse.
     * Reads the contents of the given stream and closes it.
     * 
     * @param in a stream to read in the data
     * @return the data
     * @throws IOException if an error occurs
     */
    protected static byte[] readStream(InputStream in) throws IOException {
        
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buff = new byte[4096];
        
        int len = 0;
        while ((len = in.read(buff)) == buff.length) {
            out.write(buff);
        }
        
        if (len > 0) {
            out.write(buff, 0, len);
        }
        
        in.close();
        
        return out.toByteArray();
    }
	
}